package practica1.domain;

import java.util.ArrayList;
import practica1.ui.simulacion.ThreadSimulacion;
import practica1.ui.simulacion.ThreadSimulacionListener;

/**
 * Modelo de una simulación de un plato de bacterias en 'n' días
 * @author Miguel González - Ceura
 */
public class SimulacionBacterias implements ThreadSimulacionListener {
    
    private ArrayList<SimulacionBacteriasListener> listeners;
    
    private int comidas[][][];
    private int bacterias[][][];
    private int ladoPlato;
    private int tamanioPoblacion;
    private int numDias;
    private int alimentoInicial;
    private int diaMax;
    private int alimentoMax;
    private int alimentoFinal;
    
    /**
     * Constructor por defecto, establece los siguientes valores:
     *   ladoPlato = 100
     */
    public SimulacionBacterias() {
        comidas = null;
        bacterias = null;
        ladoPlato = 100;
        
        tamanioPoblacion = 1000;
        numDias = 40;
        alimentoInicial = 150000;
        diaMax = 20;
        alimentoMax = 300000;
        alimentoFinal = 150000;
        
        listeners = new ArrayList<SimulacionBacteriasListener>();
    }
    
    public SimulacionBacterias(int[][][] bacterias,
            int[][][] comidas, int ladoPlato, int tamanioPoblacion, int numDias,
            int alimentoInicial, int diaMax, int alimentoMax, int alimentoFinal) {
        
        this.ladoPlato = ladoPlato;
        this.tamanioPoblacion = tamanioPoblacion;
        this.numDias = numDias;
        this.alimentoInicial = alimentoInicial;
        this.diaMax = diaMax;
        this.alimentoMax = alimentoMax;
        this.alimentoFinal = alimentoFinal;
        
        if(bacterias != null && comidas != null) {
            setDatosSimulacionPrivado(bacterias, comidas);
        }
        listeners = new ArrayList<SimulacionBacteriasListener>();
    }
    
    @Override
    public SimulacionBacterias clone() {
        SimulacionBacterias sim = new SimulacionBacterias(bacterias, comidas, 
                ladoPlato, tamanioPoblacion, numDias, alimentoInicial, diaMax,
                alimentoMax, alimentoFinal);
        
        return sim;
    }
    
    public void addSimulacionBacteriasListener(SimulacionBacteriasListener l) {
        if(l != null) {
            synchronized(listeners) {
                listeners.add(l);
            }
        }
    }
    
    public void removeSimulacionBacteriasListener(SimulacionBacteriasListener l) {
        if(l != null) {
            synchronized(listeners) {
                listeners.remove(l);
            }
        }
    }
    
    private void setDatosSimulacionPrivado(int[][][] bacterias,
            int[][][] comidas) {
        this.bacterias = new int[bacterias.length][][];
        
        for(int i=0; i<bacterias.length; i++) {
            this.bacterias[i] = new int[bacterias[i].length][];
            
            for(int j=0; j<bacterias[i].length; j++) {
                this.bacterias[i][j] = new int[bacterias[i][j].length];
                System.arraycopy(bacterias[i][j], 0, 
                        this.bacterias[i][j], 0 , bacterias[i][j].length);
            }
        }
        
        this.comidas = new int[comidas.length][][];
        
        for(int i=0; i<comidas.length; i++) {
            this.comidas[i] = new int[comidas[i].length][];
            
            for(int j=0; j<comidas[i].length; j++) {
                this.comidas[i][j] = new int[comidas[i][j].length];
                System.arraycopy(comidas[i][j], 0, 
                        this.comidas[i][j], 0 , comidas[i][j].length);
            }
        }
    }
    
    public void setDatosSimulacion(int[][][] bacterias,
            int[][][] comidas) {
        setDatosSimulacionPrivado(bacterias, comidas);
        
        fireSimulacionBacteriasChange();
    }
    
    public int[][][] getBacteriasPlato() {
        return bacterias;
    }
    
    public int [][] getBacteriasPlatoDia(int numDia) {
        return bacterias[numDia - 1];
    }
    
    public int[][][] getComidaPlato() {
        return comidas;
    }
    
    public int[][] getComidaPlatoDia(int numDia) {
        return comidas[numDia - 1];
    }
    
    private void fireSimulacionBacteriasChange() {
        synchronized(listeners) {
            for(SimulacionBacteriasListener l : listeners) {
                l.simulacionBacteriasChange(new SimulacionBacteriasEvent(this));
            }
        }
    }

    public void setDatosThread(ThreadSimulacion threadSimulacion) {
        bacterias = threadSimulacion.getBacteriasCalculada();
        comidas = threadSimulacion.getComidaCalculada();
        alimentoInicial = threadSimulacion.getAlimentoInicial();
        diaMax = threadSimulacion.getDiaMax();
        alimentoMax = threadSimulacion.getAlimentoMax();
        alimentoFinal = threadSimulacion.getAlimentoFinal();
        numDias = threadSimulacion.getNumDias();
        ladoPlato = threadSimulacion.getLadoPlato();
        tamanioPoblacion = threadSimulacion.getTamanioPoblacion();
        
        fireSimulacionBacteriasChange();
    }
    
    /**
     * @return the numDias
     */
    public int getNumDias() {
        return numDias;
    }

    /**
     * @param numDias the numDias to set
     */
    public void setNumDias(int numDias) {
        this.numDias = numDias;
        fireSimulacionBacteriasChange();
    }

    /**
     * @return the alimentoInicial
     */
    public int getAlimentoInicial() {
        return alimentoInicial;
    }

    /**
     * @param alimentoInicial the alimentoInicial to set
     */
    public void setAlimentoInicial(int alimentoInicial) {
        this.alimentoInicial = alimentoInicial;
        fireSimulacionBacteriasChange();
    }

    /**
     * @return the diaMax
     */
    public int getDiaMax() {
        return diaMax;
    }

    /**
     * @param diaMax the diaMax to set
     */
    public void setDiaMax(int diaMax) {
        this.diaMax = diaMax;
        fireSimulacionBacteriasChange();
    }

    /**
     * @return the alimentoMax
     */
    public int getAlimentoMax() {
        return alimentoMax;
    }

    /**
     * @param alimentoMax the alimentoMax to set
     */
    public void setAlimentoMax(int alimentoMax) {
        this.alimentoMax = alimentoMax;
        fireSimulacionBacteriasChange();
    }

    /**
     * @return the alimentoFinal
     */
    public int getAlimentoFinal() {
        return alimentoFinal;
    }

    /**
     * @param alimentoFinal the alimentoFinal to set
     */
    public void setAlimentoFinal(int alimentoFinal) {
        this.alimentoFinal = alimentoFinal;
        fireSimulacionBacteriasChange();
    }

    /**
     * @return the tamanioPoblacion
     */
    public int getTamanioPoblacion() {
        return tamanioPoblacion;
    }

    /**
     * @param tamanioPoblacion the tamanioPoblacion to set
     */
    public void setTamanioPoblacion(int tamanioPoblacion) {
        this.tamanioPoblacion = tamanioPoblacion;
        fireSimulacionBacteriasChange();
    }

    @Override
    public void terminadaSimulacion(ThreadSimulacion threadSimulacion) {
        setDatosThread(threadSimulacion);
        
        synchronized(listeners) {
            for(SimulacionBacteriasListener l : listeners) {
                l.terminadaSimulacion();
            }
        }
    }

    @Override
    public void simulacionAcabadaDia(ThreadSimulacion threadSimulacion, int numDia,
            int [][]comidaDia, int [][]bacteriasDia) {
        synchronized(listeners) {
            for(SimulacionBacteriasListener l : listeners) {
                l.simulacionAcabadaDia(numDia, comidaDia, bacteriasDia);
            }
        }
    }
    
    public int getLadoPlato() {
        return ladoPlato;
    }
    
    public void setLadoPlato(int ladoPlato) {
        this.ladoPlato = ladoPlato;
        
        fireSimulacionBacteriasChange();
    }

    @Override
    public void empezadaSimulacion(ThreadSimulacion threadSimulacion) {
        synchronized(listeners) {
            for(SimulacionBacteriasListener l : listeners) {
                l.empezadaSimulacion();
            }
        }
    }
}
